#pragma once

#include "../im.h"
#include "QueryResult.h"
#include "mysql/mysql.h"

#define IM_MYSQL_INIT MysqlConnectionPool::initInstance
#define IM_MYSQL_GET MysqlConnectionPool::getInstance()

const uint8_t MAX_RETRY_TIMES = 3;
/**
 * @brief
 * 以下是一些常见的mysql_errno错误代码：
 * 1045：访问被拒绝（用户名或密码错误）
 * 2002：无法连接到本地MySQL服务器
 * 2003：无法连接到远程MySQL服务器
 * 2005：未知的MySQL服务器主机
 * 2013：在查询期间丢失了与MySQL服务器的连接
 * 2019：获取协议信息失败
 * 2020：无法连接到MySQL服务器
 * 2026：SSL连接错误
 * 1049：未知的数据库
 */
class MysqlConnectionPool : private boost::noncopyable
{
public:
    typedef unsigned int error_code;
    typedef std::function<void(const error_code, const QueryResultPtr)> QueryCallback;
    class MysqlConnection
    {
    private:
        boost::asio::io_context &io_;
        std::thread t_;
        MYSQL *con_;
        std::string sql_;
        QueryCallback call_;
        std::condition_variable cv_;
        std::mutex mutex_;
        bool is_run;
        uint8_t retry_times;

    public:
        MysqlConnection(MYSQL *con, boost::asio::io_context &io);
        ~MysqlConnection()
        {
            t_.join();
            mysql_close(con_);
        }

        bool inline getRunning() { return is_run; }

        void inline query(std::string sql, QueryCallback call)
        {
            sql_ = sql;
            call_ = call;
            is_run = true;
            cv_.notify_one();
        }
    };

private:
    typedef std::pair<std::string, QueryCallback> mysql_event;

public:
    MysqlConnectionPool(
        boost::asio::io_context &io,
        size_t size,
        const std::string &host,
        const std::string &user,
        const std::string &pwd,
        const std::string &dbname);
    ~MysqlConnectionPool();

    static void initInstance(
        boost::asio::io_context &io,
        size_t size,
        const std::string &host,
        const std::string &user,
        const std::string &pwd,
        const std::string &dbname)
    {
        instance_ = new MysqlConnectionPool(io, size, host, user, pwd, dbname);
    }

    static inline MysqlConnectionPool *getInstance()
    {
        return instance_;
    }

    void inline async_query(
        const std::string &sql,
        QueryCallback callback)
    {
        qevents_.push({sql, callback});
        process_queries();
    }

private:
    void process_queries();

private:
    static MysqlConnectionPool *instance_;
    boost::asio::io_context &io_;
    std::vector<MYSQL *> connections_;
    std::queue<std::shared_ptr<MysqlConnection>> qconnections_;
    std::queue<mysql_event> qevents_;

    size_t size_;
    std::string host_;
    std::string user_;
    std::string pwd_;
    std::string db_;
};
